﻿/** \file
* \$Rev: 187 $
* 
* \$Date: 2010-04-27 21:58:51 +0000 (Tue, 27 Apr 2010) $
*
* \$URL: http://chorus-twitter.googlecode.com/svn/trunk/src/gui/RenameList.cs $
*/

using System;
using System.Drawing;
using System.Windows.Forms;

namespace chorus.gui
{
	/// <summary>
	/// A strongly typed generic list control that supports binding and
	/// in-place item creation, deletion and renaming.
	/// </summary>
	/// <typeparam name="DataType">The type of item that populates the list.</typeparam>
	public partial class RenameList<DataType> : ListBox
		where DataType : IDisplayRenamable, new()
	{
		/// <summary>The enumeration type for whether the naming operation is to rename or create.</summary>
		public enum NameModeEnum
		{
			/// <summary>The user is renaming an existing element.</summary>
			Rename,
			/// <summary>The user is naming a new element.</summary>
			New
		}
		/// <summary>The current naming operation mode.</summary>
		NameModeEnum namemode;
		/// <summary>The current naming operation mode.</summary>
		public NameModeEnum NameMode { get { return namemode; } }
		/// <summary>The event fired when an item is renamed.</summary>
		public event Action<DataType> RenameCurrentItem;
		/// <summary>The event fired when a new item is created.</summary>
		public event Action<DataType> NewItem;
		/// <summary>Whether to postpone data renames and additions.</summary>
		public bool Postpone { get; set; }
		/// <summary>The binding object.</summary>
		protected BindingSource Bind { get { return (BindingSource)DataSource; } }

		/// <summary>The constructor.</summary>
		public RenameList()
		{
			InitializeComponent();
			textName.Parent = this;
		}

		/// <summary>When making a new item, show the appropriate controls.</summary>
		void ShowNewItem()
		{
			textName.Text = "(new)";
			textName.Top = ItemHeight * Bind.Count;
			namemode = NameModeEnum.New;
			ShowNameBox();
		}

		/// <summary>When renaming an item, show the appropriate controls.</summary>
		void ShowRenameItem()
		{
			textName.Text = ((DataType)Bind.Current).DisplayName;
			textName.Top = ItemHeight * Bind.Position;
			namemode = NameModeEnum.Rename;
			ShowNameBox();
		}

		/// <summary>Show the naming box.</summary>
		void ShowNameBox()
		{
			textName.SelectAll();
			textName.Visible = true;
			textName.Focus();
		}

		#region Event handlers

		/// <summary>Fired when the list is resized.</summary>
		/// <param name="sender">The event sender.</param>
		/// <param name="e">The event parameters.</param>
		void RenameList_Resize(object sender, EventArgs e)
		{
			textName.Width = Width;
			textName.Height = ItemHeight;
		}

		/// <summary>Fired when the list is double-clicked.</summary>
		/// <param name="sender">The event sender.</param>
		/// <param name="e">The event parameters.</param>
		void RenameList_DoubleClick(object sender, EventArgs e)
		{
			Point p = PointToClient(Cursor.Position);
			if (IndexFromPoint(p) == -1)
				ShowNewItem();
			else
				ShowRenameItem();
		}

		/// <summary>Fired when the selection changes.</summary>
		/// <param name="sender">The event sender.</param>
		/// <param name="e">The event parameters.</param>
		void RenameList_SelectedIndexChanged(object sender, EventArgs e)
		{
			bool isActive = SelectedIndex != -1;
			menuDelete.Enabled = isActive;
			menuRename.Enabled = isActive;
		}

		/// <summary>Fired when the naming box loses focus.</summary>
		/// <param name="sender">The event sender.</param>
		/// <param name="e">The event parameters.</param>
		void textName_Leave(object sender, EventArgs e)
		{
			if (!textName.Visible)
				return;
			textName.Visible = false;
			DataType item;
			if (namemode == NameModeEnum.New)
				item = new DataType();
			else
				item = (DataType)Bind.Current;
			item.DisplayName = textName.Text;
			if (namemode == NameModeEnum.New)
			{
				if (!Postpone)
					Bind.Position = Bind.Add(item);
				if (NewItem != null)
					NewItem(item);
			}
			else
			{
				if (!Postpone)
					Bind.ResetCurrentItem();
				if (RenameCurrentItem != null)
					RenameCurrentItem(item);
			}
		}

		/// <summary>Fired when a key is pressed in the naming box.</summary>
		/// <param name="sender">The event sender.</param>
		/// <param name="e">The event parameters.</param>
		void textName_KeyDown(object sender, KeyEventArgs e)
		{
			switch (e.KeyCode)
			{
				case Keys.Escape:
				case Keys.Enter:
					e.SuppressKeyPress = true;
					Focus();
					return;
			}
		}

		/// <summary>Fired when the user creates a new item.</summary>
		/// <param name="sender">The event sender.</param>
		/// <param name="e">The event parameters.</param>
		void menuNew_Click(object sender, EventArgs e) { ShowNewItem(); }

		/// <summary>Fired when the user renames an item.</summary>
		/// <param name="sender">The event sender.</param>
		/// <param name="e">The event parameters.</param>
		void menuRename_Click(object sender, EventArgs e) { ShowRenameItem(); }

		/// <summary>Fired when the user deletes an item.</summary>
		/// <param name="sender">The event sender.</param>
		/// <param name="e">The event parameters.</param>
		void menuDelete_Click(object sender, EventArgs e) { Bind.RemoveCurrent(); }

		/// <summary>Fired when the user opens the context menu.</summary>
		/// <param name="sender">The event sender.</param>
		/// <param name="e">The event parameters.</param>
		void contextMenu_Opened(object sender, EventArgs e)
		{
			Point p = PointToClient(contextMenu.Bounds.Location);
			int index = IndexFromPoint(p);
			if (index == -1)
				Bind.MoveLast();
			else
				Bind.Position = index;
		}

		#endregion
	}

	/// <summary>A NameList containing accounts.</summary>
	public class AccountList : RenameList<twitter.Account> { }
	/// <summary>A NameList containing views.</summary>
	public class ViewList : RenameList<net.View> { }

	/// <summary>
	/// Items in RenameList must implement this interface.
	/// </summary>
	public interface IDisplayRenamable : IDisplayNamed
	{
		/// <summary>The display name of the item.</summary>
		new string DisplayName { get;  set; }
	}
}
